/* Bitstream processing
 *
 * Copyright 2020 Etienne Helluy-Lafont, Univ. Lille, CNRS.
 *
 * This file is part of Project Ubertooth.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street,
 * Boston, MA 02110-1301, USA.
 */
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <ubtbr/bb.h>

/* reverse 8 bits */
const uint8_t rev8_map[256] = { 
        0x0, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
        0x8, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
        0x4, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
        0xc, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
        0x2, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
        0xa, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
        0x6, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
        0xe, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
        0x1, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
        0x9, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
        0x5, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
        0xd, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
        0x3, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
        0xb, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
        0x7, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
        0xf, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
};

/* 8 bits of whiten word for given state */
const uint8_t whiten_word_tbl[128] = {
	0x00, 0x40, 0x20, 0x60, 0x90, 0xd0, 0xb0, 0xf0, 
	0x48, 0x08, 0x68, 0x28, 0xd8, 0x98, 0xf8, 0xb8, 
	0x24, 0x64, 0x04, 0x44, 0xb4, 0xf4, 0x94, 0xd4, 
	0x6c, 0x2c, 0x4c, 0x0c, 0xfc, 0xbc, 0xdc, 0x9c, 
	0x92, 0xd2, 0xb2, 0xf2, 0x02, 0x42, 0x22, 0x62, 
	0xda, 0x9a, 0xfa, 0xba, 0x4a, 0x0a, 0x6a, 0x2a, 
	0xb6, 0xf6, 0x96, 0xd6, 0x26, 0x66, 0x06, 0x46, 
	0xfe, 0xbe, 0xde, 0x9e, 0x6e, 0x2e, 0x4e, 0x0e, 
	0xc9, 0x89, 0xe9, 0xa9, 0x59, 0x19, 0x79, 0x39, 
	0x81, 0xc1, 0xa1, 0xe1, 0x11, 0x51, 0x31, 0x71, 
	0xed, 0xad, 0xcd, 0x8d, 0x7d, 0x3d, 0x5d, 0x1d, 
	0xa5, 0xe5, 0x85, 0xc5, 0x35, 0x75, 0x15, 0x55, 
	0x5b, 0x1b, 0x7b, 0x3b, 0xcb, 0x8b, 0xeb, 0xab, 
	0x13, 0x53, 0x33, 0x73, 0x83, 0xc3, 0xa3, 0xe3, 
	0x7f, 0x3f, 0x5f, 0x1f, 0xef, 0xaf, 0xcf, 0x8f, 
	0x37, 0x77, 0x17, 0x57, 0xa7, 0xe7, 0x87, 0xc7, 
};

/* Advance whiten state by 1 to 8 steps */
const uint8_t whiten_state_tbl[8][128] = {
{0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e, 
0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e, 
0x40, 0x42, 0x44, 0x46, 0x48, 0x4a, 0x4c, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e, 
0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7e, 
0x11, 0x13, 0x15, 0x17, 0x19, 0x1b, 0x1d, 0x1f, 0x01, 0x03, 0x05, 0x07, 0x09, 0x0b, 0x0d, 0x0f, 
0x31, 0x33, 0x35, 0x37, 0x39, 0x3b, 0x3d, 0x3f, 0x21, 0x23, 0x25, 0x27, 0x29, 0x2b, 0x2d, 0x2f, 
0x51, 0x53, 0x55, 0x57, 0x59, 0x5b, 0x5d, 0x5f, 0x41, 0x43, 0x45, 0x47, 0x49, 0x4b, 0x4d, 0x4f, 
0x71, 0x73, 0x75, 0x77, 0x79, 0x7b, 0x7d, 0x7f, 0x61, 0x63, 0x65, 0x67, 0x69, 0x6b, 0x6d, 0x6f, 
},
{0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c, 0x20, 0x24, 0x28, 0x2c, 0x30, 0x34, 0x38, 0x3c, 
0x40, 0x44, 0x48, 0x4c, 0x50, 0x54, 0x58, 0x5c, 0x60, 0x64, 0x68, 0x6c, 0x70, 0x74, 0x78, 0x7c, 
0x11, 0x15, 0x19, 0x1d, 0x01, 0x05, 0x09, 0x0d, 0x31, 0x35, 0x39, 0x3d, 0x21, 0x25, 0x29, 0x2d, 
0x51, 0x55, 0x59, 0x5d, 0x41, 0x45, 0x49, 0x4d, 0x71, 0x75, 0x79, 0x7d, 0x61, 0x65, 0x69, 0x6d, 
0x22, 0x26, 0x2a, 0x2e, 0x32, 0x36, 0x3a, 0x3e, 0x02, 0x06, 0x0a, 0x0e, 0x12, 0x16, 0x1a, 0x1e, 
0x62, 0x66, 0x6a, 0x6e, 0x72, 0x76, 0x7a, 0x7e, 0x42, 0x46, 0x4a, 0x4e, 0x52, 0x56, 0x5a, 0x5e, 
0x33, 0x37, 0x3b, 0x3f, 0x23, 0x27, 0x2b, 0x2f, 0x13, 0x17, 0x1b, 0x1f, 0x03, 0x07, 0x0b, 0x0f, 
0x73, 0x77, 0x7b, 0x7f, 0x63, 0x67, 0x6b, 0x6f, 0x53, 0x57, 0x5b, 0x5f, 0x43, 0x47, 0x4b, 0x4f, 
},
{0x00, 0x08, 0x10, 0x18, 0x20, 0x28, 0x30, 0x38, 0x40, 0x48, 0x50, 0x58, 0x60, 0x68, 0x70, 0x78, 
0x11, 0x19, 0x01, 0x09, 0x31, 0x39, 0x21, 0x29, 0x51, 0x59, 0x41, 0x49, 0x71, 0x79, 0x61, 0x69, 
0x22, 0x2a, 0x32, 0x3a, 0x02, 0x0a, 0x12, 0x1a, 0x62, 0x6a, 0x72, 0x7a, 0x42, 0x4a, 0x52, 0x5a, 
0x33, 0x3b, 0x23, 0x2b, 0x13, 0x1b, 0x03, 0x0b, 0x73, 0x7b, 0x63, 0x6b, 0x53, 0x5b, 0x43, 0x4b, 
0x44, 0x4c, 0x54, 0x5c, 0x64, 0x6c, 0x74, 0x7c, 0x04, 0x0c, 0x14, 0x1c, 0x24, 0x2c, 0x34, 0x3c, 
0x55, 0x5d, 0x45, 0x4d, 0x75, 0x7d, 0x65, 0x6d, 0x15, 0x1d, 0x05, 0x0d, 0x35, 0x3d, 0x25, 0x2d, 
0x66, 0x6e, 0x76, 0x7e, 0x46, 0x4e, 0x56, 0x5e, 0x26, 0x2e, 0x36, 0x3e, 0x06, 0x0e, 0x16, 0x1e, 
0x77, 0x7f, 0x67, 0x6f, 0x57, 0x5f, 0x47, 0x4f, 0x37, 0x3f, 0x27, 0x2f, 0x17, 0x1f, 0x07, 0x0f, 
},
{0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x11, 0x01, 0x31, 0x21, 0x51, 0x41, 0x71, 0x61, 
0x22, 0x32, 0x02, 0x12, 0x62, 0x72, 0x42, 0x52, 0x33, 0x23, 0x13, 0x03, 0x73, 0x63, 0x53, 0x43, 
0x44, 0x54, 0x64, 0x74, 0x04, 0x14, 0x24, 0x34, 0x55, 0x45, 0x75, 0x65, 0x15, 0x05, 0x35, 0x25, 
0x66, 0x76, 0x46, 0x56, 0x26, 0x36, 0x06, 0x16, 0x77, 0x67, 0x57, 0x47, 0x37, 0x27, 0x17, 0x07, 
0x19, 0x09, 0x39, 0x29, 0x59, 0x49, 0x79, 0x69, 0x08, 0x18, 0x28, 0x38, 0x48, 0x58, 0x68, 0x78, 
0x3b, 0x2b, 0x1b, 0x0b, 0x7b, 0x6b, 0x5b, 0x4b, 0x2a, 0x3a, 0x0a, 0x1a, 0x6a, 0x7a, 0x4a, 0x5a, 
0x5d, 0x4d, 0x7d, 0x6d, 0x1d, 0x0d, 0x3d, 0x2d, 0x4c, 0x5c, 0x6c, 0x7c, 0x0c, 0x1c, 0x2c, 0x3c, 
0x7f, 0x6f, 0x5f, 0x4f, 0x3f, 0x2f, 0x1f, 0x0f, 0x6e, 0x7e, 0x4e, 0x5e, 0x2e, 0x3e, 0x0e, 0x1e, 
},
{0x00, 0x20, 0x40, 0x60, 0x11, 0x31, 0x51, 0x71, 0x22, 0x02, 0x62, 0x42, 0x33, 0x13, 0x73, 0x53, 
0x44, 0x64, 0x04, 0x24, 0x55, 0x75, 0x15, 0x35, 0x66, 0x46, 0x26, 0x06, 0x77, 0x57, 0x37, 0x17, 
0x19, 0x39, 0x59, 0x79, 0x08, 0x28, 0x48, 0x68, 0x3b, 0x1b, 0x7b, 0x5b, 0x2a, 0x0a, 0x6a, 0x4a, 
0x5d, 0x7d, 0x1d, 0x3d, 0x4c, 0x6c, 0x0c, 0x2c, 0x7f, 0x5f, 0x3f, 0x1f, 0x6e, 0x4e, 0x2e, 0x0e, 
0x32, 0x12, 0x72, 0x52, 0x23, 0x03, 0x63, 0x43, 0x10, 0x30, 0x50, 0x70, 0x01, 0x21, 0x41, 0x61, 
0x76, 0x56, 0x36, 0x16, 0x67, 0x47, 0x27, 0x07, 0x54, 0x74, 0x14, 0x34, 0x45, 0x65, 0x05, 0x25, 
0x2b, 0x0b, 0x6b, 0x4b, 0x3a, 0x1a, 0x7a, 0x5a, 0x09, 0x29, 0x49, 0x69, 0x18, 0x38, 0x58, 0x78, 
0x6f, 0x4f, 0x2f, 0x0f, 0x7e, 0x5e, 0x3e, 0x1e, 0x4d, 0x6d, 0x0d, 0x2d, 0x5c, 0x7c, 0x1c, 0x3c, 
},
{0x00, 0x40, 0x11, 0x51, 0x22, 0x62, 0x33, 0x73, 0x44, 0x04, 0x55, 0x15, 0x66, 0x26, 0x77, 0x37, 
0x19, 0x59, 0x08, 0x48, 0x3b, 0x7b, 0x2a, 0x6a, 0x5d, 0x1d, 0x4c, 0x0c, 0x7f, 0x3f, 0x6e, 0x2e, 
0x32, 0x72, 0x23, 0x63, 0x10, 0x50, 0x01, 0x41, 0x76, 0x36, 0x67, 0x27, 0x54, 0x14, 0x45, 0x05, 
0x2b, 0x6b, 0x3a, 0x7a, 0x09, 0x49, 0x18, 0x58, 0x6f, 0x2f, 0x7e, 0x3e, 0x4d, 0x0d, 0x5c, 0x1c, 
0x64, 0x24, 0x75, 0x35, 0x46, 0x06, 0x57, 0x17, 0x20, 0x60, 0x31, 0x71, 0x02, 0x42, 0x13, 0x53, 
0x7d, 0x3d, 0x6c, 0x2c, 0x5f, 0x1f, 0x4e, 0x0e, 0x39, 0x79, 0x28, 0x68, 0x1b, 0x5b, 0x0a, 0x4a, 
0x56, 0x16, 0x47, 0x07, 0x74, 0x34, 0x65, 0x25, 0x12, 0x52, 0x03, 0x43, 0x30, 0x70, 0x21, 0x61, 
0x4f, 0x0f, 0x5e, 0x1e, 0x6d, 0x2d, 0x7c, 0x3c, 0x0b, 0x4b, 0x1a, 0x5a, 0x29, 0x69, 0x38, 0x78, 
},
{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x19, 0x08, 0x3b, 0x2a, 0x5d, 0x4c, 0x7f, 0x6e, 
0x32, 0x23, 0x10, 0x01, 0x76, 0x67, 0x54, 0x45, 0x2b, 0x3a, 0x09, 0x18, 0x6f, 0x7e, 0x4d, 0x5c, 
0x64, 0x75, 0x46, 0x57, 0x20, 0x31, 0x02, 0x13, 0x7d, 0x6c, 0x5f, 0x4e, 0x39, 0x28, 0x1b, 0x0a, 
0x56, 0x47, 0x74, 0x65, 0x12, 0x03, 0x30, 0x21, 0x4f, 0x5e, 0x6d, 0x7c, 0x0b, 0x1a, 0x29, 0x38, 
0x59, 0x48, 0x7b, 0x6a, 0x1d, 0x0c, 0x3f, 0x2e, 0x40, 0x51, 0x62, 0x73, 0x04, 0x15, 0x26, 0x37, 
0x6b, 0x7a, 0x49, 0x58, 0x2f, 0x3e, 0x0d, 0x1c, 0x72, 0x63, 0x50, 0x41, 0x36, 0x27, 0x14, 0x05, 
0x3d, 0x2c, 0x1f, 0x0e, 0x79, 0x68, 0x5b, 0x4a, 0x24, 0x35, 0x06, 0x17, 0x60, 0x71, 0x42, 0x53, 
0x0f, 0x1e, 0x2d, 0x3c, 0x4b, 0x5a, 0x69, 0x78, 0x16, 0x07, 0x34, 0x25, 0x52, 0x43, 0x70, 0x61, 
},
{0x00, 0x22, 0x44, 0x66, 0x19, 0x3b, 0x5d, 0x7f, 0x32, 0x10, 0x76, 0x54, 0x2b, 0x09, 0x6f, 0x4d, 
0x64, 0x46, 0x20, 0x02, 0x7d, 0x5f, 0x39, 0x1b, 0x56, 0x74, 0x12, 0x30, 0x4f, 0x6d, 0x0b, 0x29, 
0x59, 0x7b, 0x1d, 0x3f, 0x40, 0x62, 0x04, 0x26, 0x6b, 0x49, 0x2f, 0x0d, 0x72, 0x50, 0x36, 0x14, 
0x3d, 0x1f, 0x79, 0x5b, 0x24, 0x06, 0x60, 0x42, 0x0f, 0x2d, 0x4b, 0x69, 0x16, 0x34, 0x52, 0x70, 
0x23, 0x01, 0x67, 0x45, 0x3a, 0x18, 0x7e, 0x5c, 0x11, 0x33, 0x55, 0x77, 0x08, 0x2a, 0x4c, 0x6e, 
0x47, 0x65, 0x03, 0x21, 0x5e, 0x7c, 0x1a, 0x38, 0x75, 0x57, 0x31, 0x13, 0x6c, 0x4e, 0x28, 0x0a, 
0x7a, 0x58, 0x3e, 0x1c, 0x63, 0x41, 0x27, 0x05, 0x48, 0x6a, 0x0c, 0x2e, 0x51, 0x73, 0x15, 0x37, 
0x1e, 0x3c, 0x5a, 0x78, 0x07, 0x25, 0x43, 0x61, 0x2c, 0x0e, 0x68, 0x4a, 0x35, 0x17, 0x71, 0x53, 
}};

/* F13 Lut to encode 8 bits */
const uint32_t fec13_tbl[256] = {
	0x000000, 0x000007, 0x000038, 0x00003f, 0x0001c0, 0x0001c7, 0x0001f8, 0x0001ff, 
	0x000e00, 0x000e07, 0x000e38, 0x000e3f, 0x000fc0, 0x000fc7, 0x000ff8, 0x000fff, 
	0x007000, 0x007007, 0x007038, 0x00703f, 0x0071c0, 0x0071c7, 0x0071f8, 0x0071ff, 
	0x007e00, 0x007e07, 0x007e38, 0x007e3f, 0x007fc0, 0x007fc7, 0x007ff8, 0x007fff, 
	0x038000, 0x038007, 0x038038, 0x03803f, 0x0381c0, 0x0381c7, 0x0381f8, 0x0381ff, 
	0x038e00, 0x038e07, 0x038e38, 0x038e3f, 0x038fc0, 0x038fc7, 0x038ff8, 0x038fff, 
	0x03f000, 0x03f007, 0x03f038, 0x03f03f, 0x03f1c0, 0x03f1c7, 0x03f1f8, 0x03f1ff, 
	0x03fe00, 0x03fe07, 0x03fe38, 0x03fe3f, 0x03ffc0, 0x03ffc7, 0x03fff8, 0x03ffff, 
	0x1c0000, 0x1c0007, 0x1c0038, 0x1c003f, 0x1c01c0, 0x1c01c7, 0x1c01f8, 0x1c01ff, 
	0x1c0e00, 0x1c0e07, 0x1c0e38, 0x1c0e3f, 0x1c0fc0, 0x1c0fc7, 0x1c0ff8, 0x1c0fff, 
	0x1c7000, 0x1c7007, 0x1c7038, 0x1c703f, 0x1c71c0, 0x1c71c7, 0x1c71f8, 0x1c71ff, 
	0x1c7e00, 0x1c7e07, 0x1c7e38, 0x1c7e3f, 0x1c7fc0, 0x1c7fc7, 0x1c7ff8, 0x1c7fff, 
	0x1f8000, 0x1f8007, 0x1f8038, 0x1f803f, 0x1f81c0, 0x1f81c7, 0x1f81f8, 0x1f81ff, 
	0x1f8e00, 0x1f8e07, 0x1f8e38, 0x1f8e3f, 0x1f8fc0, 0x1f8fc7, 0x1f8ff8, 0x1f8fff, 
	0x1ff000, 0x1ff007, 0x1ff038, 0x1ff03f, 0x1ff1c0, 0x1ff1c7, 0x1ff1f8, 0x1ff1ff, 
	0x1ffe00, 0x1ffe07, 0x1ffe38, 0x1ffe3f, 0x1fffc0, 0x1fffc7, 0x1ffff8, 0x1fffff, 
	0xe00000, 0xe00007, 0xe00038, 0xe0003f, 0xe001c0, 0xe001c7, 0xe001f8, 0xe001ff, 
	0xe00e00, 0xe00e07, 0xe00e38, 0xe00e3f, 0xe00fc0, 0xe00fc7, 0xe00ff8, 0xe00fff, 
	0xe07000, 0xe07007, 0xe07038, 0xe0703f, 0xe071c0, 0xe071c7, 0xe071f8, 0xe071ff, 
	0xe07e00, 0xe07e07, 0xe07e38, 0xe07e3f, 0xe07fc0, 0xe07fc7, 0xe07ff8, 0xe07fff, 
	0xe38000, 0xe38007, 0xe38038, 0xe3803f, 0xe381c0, 0xe381c7, 0xe381f8, 0xe381ff, 
	0xe38e00, 0xe38e07, 0xe38e38, 0xe38e3f, 0xe38fc0, 0xe38fc7, 0xe38ff8, 0xe38fff, 
	0xe3f000, 0xe3f007, 0xe3f038, 0xe3f03f, 0xe3f1c0, 0xe3f1c7, 0xe3f1f8, 0xe3f1ff, 
	0xe3fe00, 0xe3fe07, 0xe3fe38, 0xe3fe3f, 0xe3ffc0, 0xe3ffc7, 0xe3fff8, 0xe3ffff, 
	0xfc0000, 0xfc0007, 0xfc0038, 0xfc003f, 0xfc01c0, 0xfc01c7, 0xfc01f8, 0xfc01ff, 
	0xfc0e00, 0xfc0e07, 0xfc0e38, 0xfc0e3f, 0xfc0fc0, 0xfc0fc7, 0xfc0ff8, 0xfc0fff, 
	0xfc7000, 0xfc7007, 0xfc7038, 0xfc703f, 0xfc71c0, 0xfc71c7, 0xfc71f8, 0xfc71ff, 
	0xfc7e00, 0xfc7e07, 0xfc7e38, 0xfc7e3f, 0xfc7fc0, 0xfc7fc7, 0xfc7ff8, 0xfc7fff, 
	0xff8000, 0xff8007, 0xff8038, 0xff803f, 0xff81c0, 0xff81c7, 0xff81f8, 0xff81ff, 
	0xff8e00, 0xff8e07, 0xff8e38, 0xff8e3f, 0xff8fc0, 0xff8fc7, 0xff8ff8, 0xff8fff, 
	0xfff000, 0xfff007, 0xfff038, 0xfff03f, 0xfff1c0, 0xfff1c7, 0xfff1f8, 0xfff1ff, 
	0xfffe00, 0xfffe07, 0xfffe38, 0xfffe3f, 0xffffc0, 0xffffc7, 0xfffff8, 0xffffff, 
};

/* Fec13 lookup table to decode 6 bits */
const uint16_t unfec13_tbl[64] = {
	0x0000, 0x4000, 0x4000, 0x4001, 0x4000, 0x4001, 0x4001, 0x0001, 
	0x4000, 0x8000, 0x8000, 0x8001, 0x8000, 0x8001, 0x8001, 0x4001, 
	0x4000, 0x8000, 0x8000, 0x8001, 0x8000, 0x8001, 0x8001, 0x4001, 
	0x4002, 0x8002, 0x8002, 0x8003, 0x8002, 0x8003, 0x8003, 0x4003, 
	0x4000, 0x8000, 0x8000, 0x8001, 0x8000, 0x8001, 0x8001, 0x4001, 
	0x4002, 0x8002, 0x8002, 0x8003, 0x8002, 0x8003, 0x8003, 0x4003, 
	0x4002, 0x8002, 0x8002, 0x8003, 0x8002, 0x8003, 0x8003, 0x4003, 
	0x0002, 0x4002, 0x4002, 0x4003, 0x4002, 0x4003, 0x4003, 0x0003, 
};

/* fec23 lut: codeword for given data */
const uint8_t fec23_tbl[1024] = {
	0x00, 0x0b, 0x16, 0x1d, 0x07, 0x0c, 0x11, 0x1a, 0x0e, 0x05, 0x18, 0x13, 0x09, 0x02, 0x1f, 0x14, 
	0x1c, 0x17, 0x0a, 0x01, 0x1b, 0x10, 0x0d, 0x06, 0x12, 0x19, 0x04, 0x0f, 0x15, 0x1e, 0x03, 0x08, 
	0x13, 0x18, 0x05, 0x0e, 0x14, 0x1f, 0x02, 0x09, 0x1d, 0x16, 0x0b, 0x00, 0x1a, 0x11, 0x0c, 0x07, 
	0x0f, 0x04, 0x19, 0x12, 0x08, 0x03, 0x1e, 0x15, 0x01, 0x0a, 0x17, 0x1c, 0x06, 0x0d, 0x10, 0x1b, 
	0x0d, 0x06, 0x1b, 0x10, 0x0a, 0x01, 0x1c, 0x17, 0x03, 0x08, 0x15, 0x1e, 0x04, 0x0f, 0x12, 0x19, 
	0x11, 0x1a, 0x07, 0x0c, 0x16, 0x1d, 0x00, 0x0b, 0x1f, 0x14, 0x09, 0x02, 0x18, 0x13, 0x0e, 0x05, 
	0x1e, 0x15, 0x08, 0x03, 0x19, 0x12, 0x0f, 0x04, 0x10, 0x1b, 0x06, 0x0d, 0x17, 0x1c, 0x01, 0x0a, 
	0x02, 0x09, 0x14, 0x1f, 0x05, 0x0e, 0x13, 0x18, 0x0c, 0x07, 0x1a, 0x11, 0x0b, 0x00, 0x1d, 0x16, 
	0x1a, 0x11, 0x0c, 0x07, 0x1d, 0x16, 0x0b, 0x00, 0x14, 0x1f, 0x02, 0x09, 0x13, 0x18, 0x05, 0x0e, 
	0x06, 0x0d, 0x10, 0x1b, 0x01, 0x0a, 0x17, 0x1c, 0x08, 0x03, 0x1e, 0x15, 0x0f, 0x04, 0x19, 0x12, 
	0x09, 0x02, 0x1f, 0x14, 0x0e, 0x05, 0x18, 0x13, 0x07, 0x0c, 0x11, 0x1a, 0x00, 0x0b, 0x16, 0x1d, 
	0x15, 0x1e, 0x03, 0x08, 0x12, 0x19, 0x04, 0x0f, 0x1b, 0x10, 0x0d, 0x06, 0x1c, 0x17, 0x0a, 0x01, 
	0x17, 0x1c, 0x01, 0x0a, 0x10, 0x1b, 0x06, 0x0d, 0x19, 0x12, 0x0f, 0x04, 0x1e, 0x15, 0x08, 0x03, 
	0x0b, 0x00, 0x1d, 0x16, 0x0c, 0x07, 0x1a, 0x11, 0x05, 0x0e, 0x13, 0x18, 0x02, 0x09, 0x14, 0x1f, 
	0x04, 0x0f, 0x12, 0x19, 0x03, 0x08, 0x15, 0x1e, 0x0a, 0x01, 0x1c, 0x17, 0x0d, 0x06, 0x1b, 0x10, 
	0x18, 0x13, 0x0e, 0x05, 0x1f, 0x14, 0x09, 0x02, 0x16, 0x1d, 0x00, 0x0b, 0x11, 0x1a, 0x07, 0x0c, 
	0x1f, 0x14, 0x09, 0x02, 0x18, 0x13, 0x0e, 0x05, 0x11, 0x1a, 0x07, 0x0c, 0x16, 0x1d, 0x00, 0x0b, 
	0x03, 0x08, 0x15, 0x1e, 0x04, 0x0f, 0x12, 0x19, 0x0d, 0x06, 0x1b, 0x10, 0x0a, 0x01, 0x1c, 0x17, 
	0x0c, 0x07, 0x1a, 0x11, 0x0b, 0x00, 0x1d, 0x16, 0x02, 0x09, 0x14, 0x1f, 0x05, 0x0e, 0x13, 0x18, 
	0x10, 0x1b, 0x06, 0x0d, 0x17, 0x1c, 0x01, 0x0a, 0x1e, 0x15, 0x08, 0x03, 0x19, 0x12, 0x0f, 0x04, 
	0x12, 0x19, 0x04, 0x0f, 0x15, 0x1e, 0x03, 0x08, 0x1c, 0x17, 0x0a, 0x01, 0x1b, 0x10, 0x0d, 0x06, 
	0x0e, 0x05, 0x18, 0x13, 0x09, 0x02, 0x1f, 0x14, 0x00, 0x0b, 0x16, 0x1d, 0x07, 0x0c, 0x11, 0x1a, 
	0x01, 0x0a, 0x17, 0x1c, 0x06, 0x0d, 0x10, 0x1b, 0x0f, 0x04, 0x19, 0x12, 0x08, 0x03, 0x1e, 0x15, 
	0x1d, 0x16, 0x0b, 0x00, 0x1a, 0x11, 0x0c, 0x07, 0x13, 0x18, 0x05, 0x0e, 0x14, 0x1f, 0x02, 0x09, 
	0x05, 0x0e, 0x13, 0x18, 0x02, 0x09, 0x14, 0x1f, 0x0b, 0x00, 0x1d, 0x16, 0x0c, 0x07, 0x1a, 0x11, 
	0x19, 0x12, 0x0f, 0x04, 0x1e, 0x15, 0x08, 0x03, 0x17, 0x1c, 0x01, 0x0a, 0x10, 0x1b, 0x06, 0x0d, 
	0x16, 0x1d, 0x00, 0x0b, 0x11, 0x1a, 0x07, 0x0c, 0x18, 0x13, 0x0e, 0x05, 0x1f, 0x14, 0x09, 0x02, 
	0x0a, 0x01, 0x1c, 0x17, 0x0d, 0x06, 0x1b, 0x10, 0x04, 0x0f, 0x12, 0x19, 0x03, 0x08, 0x15, 0x1e, 
	0x08, 0x03, 0x1e, 0x15, 0x0f, 0x04, 0x19, 0x12, 0x06, 0x0d, 0x10, 0x1b, 0x01, 0x0a, 0x17, 0x1c, 
	0x14, 0x1f, 0x02, 0x09, 0x13, 0x18, 0x05, 0x0e, 0x1a, 0x11, 0x0c, 0x07, 0x1d, 0x16, 0x0b, 0x00, 
	0x1b, 0x10, 0x0d, 0x06, 0x1c, 0x17, 0x0a, 0x01, 0x15, 0x1e, 0x03, 0x08, 0x12, 0x19, 0x04, 0x0f, 
	0x07, 0x0c, 0x11, 0x1a, 0x00, 0x0b, 0x16, 0x1d, 0x09, 0x02, 0x1f, 0x14, 0x0e, 0x05, 0x18, 0x13, 
	0x15, 0x1e, 0x03, 0x08, 0x12, 0x19, 0x04, 0x0f, 0x1b, 0x10, 0x0d, 0x06, 0x1c, 0x17, 0x0a, 0x01, 
	0x09, 0x02, 0x1f, 0x14, 0x0e, 0x05, 0x18, 0x13, 0x07, 0x0c, 0x11, 0x1a, 0x00, 0x0b, 0x16, 0x1d, 
	0x06, 0x0d, 0x10, 0x1b, 0x01, 0x0a, 0x17, 0x1c, 0x08, 0x03, 0x1e, 0x15, 0x0f, 0x04, 0x19, 0x12, 
	0x1a, 0x11, 0x0c, 0x07, 0x1d, 0x16, 0x0b, 0x00, 0x14, 0x1f, 0x02, 0x09, 0x13, 0x18, 0x05, 0x0e, 
	0x18, 0x13, 0x0e, 0x05, 0x1f, 0x14, 0x09, 0x02, 0x16, 0x1d, 0x00, 0x0b, 0x11, 0x1a, 0x07, 0x0c, 
	0x04, 0x0f, 0x12, 0x19, 0x03, 0x08, 0x15, 0x1e, 0x0a, 0x01, 0x1c, 0x17, 0x0d, 0x06, 0x1b, 0x10, 
	0x0b, 0x00, 0x1d, 0x16, 0x0c, 0x07, 0x1a, 0x11, 0x05, 0x0e, 0x13, 0x18, 0x02, 0x09, 0x14, 0x1f, 
	0x17, 0x1c, 0x01, 0x0a, 0x10, 0x1b, 0x06, 0x0d, 0x19, 0x12, 0x0f, 0x04, 0x1e, 0x15, 0x08, 0x03, 
	0x0f, 0x04, 0x19, 0x12, 0x08, 0x03, 0x1e, 0x15, 0x01, 0x0a, 0x17, 0x1c, 0x06, 0x0d, 0x10, 0x1b, 
	0x13, 0x18, 0x05, 0x0e, 0x14, 0x1f, 0x02, 0x09, 0x1d, 0x16, 0x0b, 0x00, 0x1a, 0x11, 0x0c, 0x07, 
	0x1c, 0x17, 0x0a, 0x01, 0x1b, 0x10, 0x0d, 0x06, 0x12, 0x19, 0x04, 0x0f, 0x15, 0x1e, 0x03, 0x08, 
	0x00, 0x0b, 0x16, 0x1d, 0x07, 0x0c, 0x11, 0x1a, 0x0e, 0x05, 0x18, 0x13, 0x09, 0x02, 0x1f, 0x14, 
	0x02, 0x09, 0x14, 0x1f, 0x05, 0x0e, 0x13, 0x18, 0x0c, 0x07, 0x1a, 0x11, 0x0b, 0x00, 0x1d, 0x16, 
	0x1e, 0x15, 0x08, 0x03, 0x19, 0x12, 0x0f, 0x04, 0x10, 0x1b, 0x06, 0x0d, 0x17, 0x1c, 0x01, 0x0a, 
	0x11, 0x1a, 0x07, 0x0c, 0x16, 0x1d, 0x00, 0x0b, 0x1f, 0x14, 0x09, 0x02, 0x18, 0x13, 0x0e, 0x05, 
	0x0d, 0x06, 0x1b, 0x10, 0x0a, 0x01, 0x1c, 0x17, 0x03, 0x08, 0x15, 0x1e, 0x04, 0x0f, 0x12, 0x19, 
	0x0a, 0x01, 0x1c, 0x17, 0x0d, 0x06, 0x1b, 0x10, 0x04, 0x0f, 0x12, 0x19, 0x03, 0x08, 0x15, 0x1e, 
	0x16, 0x1d, 0x00, 0x0b, 0x11, 0x1a, 0x07, 0x0c, 0x18, 0x13, 0x0e, 0x05, 0x1f, 0x14, 0x09, 0x02, 
	0x19, 0x12, 0x0f, 0x04, 0x1e, 0x15, 0x08, 0x03, 0x17, 0x1c, 0x01, 0x0a, 0x10, 0x1b, 0x06, 0x0d, 
	0x05, 0x0e, 0x13, 0x18, 0x02, 0x09, 0x14, 0x1f, 0x0b, 0x00, 0x1d, 0x16, 0x0c, 0x07, 0x1a, 0x11, 
	0x07, 0x0c, 0x11, 0x1a, 0x00, 0x0b, 0x16, 0x1d, 0x09, 0x02, 0x1f, 0x14, 0x0e, 0x05, 0x18, 0x13, 
	0x1b, 0x10, 0x0d, 0x06, 0x1c, 0x17, 0x0a, 0x01, 0x15, 0x1e, 0x03, 0x08, 0x12, 0x19, 0x04, 0x0f, 
	0x14, 0x1f, 0x02, 0x09, 0x13, 0x18, 0x05, 0x0e, 0x1a, 0x11, 0x0c, 0x07, 0x1d, 0x16, 0x0b, 0x00, 
	0x08, 0x03, 0x1e, 0x15, 0x0f, 0x04, 0x19, 0x12, 0x06, 0x0d, 0x10, 0x1b, 0x01, 0x0a, 0x17, 0x1c, 
	0x10, 0x1b, 0x06, 0x0d, 0x17, 0x1c, 0x01, 0x0a, 0x1e, 0x15, 0x08, 0x03, 0x19, 0x12, 0x0f, 0x04, 
	0x0c, 0x07, 0x1a, 0x11, 0x0b, 0x00, 0x1d, 0x16, 0x02, 0x09, 0x14, 0x1f, 0x05, 0x0e, 0x13, 0x18, 
	0x03, 0x08, 0x15, 0x1e, 0x04, 0x0f, 0x12, 0x19, 0x0d, 0x06, 0x1b, 0x10, 0x0a, 0x01, 0x1c, 0x17, 
	0x1f, 0x14, 0x09, 0x02, 0x18, 0x13, 0x0e, 0x05, 0x11, 0x1a, 0x07, 0x0c, 0x16, 0x1d, 0x00, 0x0b, 
	0x1d, 0x16, 0x0b, 0x00, 0x1a, 0x11, 0x0c, 0x07, 0x13, 0x18, 0x05, 0x0e, 0x14, 0x1f, 0x02, 0x09, 
	0x01, 0x0a, 0x17, 0x1c, 0x06, 0x0d, 0x10, 0x1b, 0x0f, 0x04, 0x19, 0x12, 0x08, 0x03, 0x1e, 0x15, 
	0x0e, 0x05, 0x18, 0x13, 0x09, 0x02, 0x1f, 0x14, 0x00, 0x0b, 0x16, 0x1d, 0x07, 0x0c, 0x11, 0x1a, 
	0x12, 0x19, 0x04, 0x0f, 0x15, 0x1e, 0x03, 0x08, 0x1c, 0x17, 0x0a, 0x01, 0x1b, 0x10, 0x0d, 0x06, 
};

const uint16_t crc_tbl[256] = {
	0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 
	0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 
	0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, 
	0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 
	0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, 
	0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, 
	0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 
	0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, 
	0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, 
	0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 
	0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, 
	0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, 
	0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 
	0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 
	0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, 
	0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, 
	0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 
	0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, 
	0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 
	0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 
	0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, 
	0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 
	0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, 
	0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, 
	0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 
	0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, 
	0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, 
	0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, 
	0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, 
	0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, 
	0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 
	0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78, 
};
const uint8_t hec_tbl[32] = {
	0x00, 0xce, 0x57, 0x99, 0xae, 0x60, 0xf9, 0x37, 
	0x97, 0x59, 0xc0, 0x0e, 0x39, 0xf7, 0x6e, 0xa0, 
	0xe5, 0x2b, 0xb2, 0x7c, 0x4b, 0x85, 0x1c, 0xd2, 
	0x72, 0xbc, 0x25, 0xeb, 0xdc, 0x12, 0x8b, 0x45, 
};

void null_decode(uint8_t *out, uint8_t *in, unsigned in_of, unsigned byte_count)
{
	unsigned i;
	uint8_t b, byte_of;

	if (byte_count == 0)
		return;

	byte_of = in_of/8;
	in_of &= 7;
	in += byte_of;
	if (in_of == 0)
	{
		memcpy(out, in, byte_count);
		return;
	}
	if (in_of != 2)
		DIE("nyi unaligned in %d\n", in_of);

	for (i=0;i<byte_count;i++)
	{
		out[i] = 0xff & ((in[i]>>2)|(in[i+1]<<6));
	}
}

void null_encode(uint8_t *out, uint8_t *in, unsigned out_of, unsigned byte_count)
{
	unsigned i;
	uint8_t b, byte_of;

	if (byte_count == 0)
		return;
	
	byte_of = out_of/8;
	out_of &= 7;
	out += byte_of;
	if (out_of == 0)
	{
		memcpy(out, in, byte_count);
		return;
	}
	if (out_of != 2)
		DIE("nyi unaligned out %d\n", out_of);

	out[0] |= 0xff & (in[0]<<2);
	for (i=0;i<byte_count-1;i++)
	{
		out[i+1] = 0xff & ((in[i]>>6)|(in[i+1]<<2));
	}
	// last bits
	out[i+1] = (in[i]>>6);
}

/* (Un)whiten n bits from input to output, possibly in place*/
void whiten(uint8_t* output, uint8_t* input, int length, uint8_t *statep)
{
	unsigned i;
	uint8_t state = *statep;
	unsigned nbytes = length>>3;
	unsigned nbits = length&7;

	state &= 0x7f;

	for(i=0;i<nbytes;i++)
	{
		output[i] = input[i]^whiten_word_tbl[state];
		state = whiten_state_tbl[7][state];
	}
	if (nbits)
	{
		output[i] = (input[i]^whiten_word_tbl[state]) & ((1<<nbits)-1);

		/* Advance state */
		state = whiten_state_tbl[nbits-1][state];
	}
	*statep = state;
}

/* Fec23 
 * out: out buffer of BYTE_ALIGNED(nbits*15/10)
 * in : input buffer of nbits
 * out_of: output offset in bits
 * nbits: size of input in bits
*/
int fec23(uint8_t *out, uint8_t *in, unsigned out_of, unsigned nbits)
{
	unsigned in_bit_pos, byte_pos, out_bit_pos, data;
	unsigned nout = ((nbits+9)/10)*15;

	// 10 bits in -> 15 output bits
	for(in_bit_pos=0, out_bit_pos=out_of
		; in_bit_pos<nout
		; in_bit_pos+=10, out_bit_pos+=15)
	{
		byte_pos =in_bit_pos>>3;
		switch(in_bit_pos&7) {
		case 0: data = EXTRACT10_0(in+byte_pos); break;
		case 1: data = EXTRACT10_1(in+byte_pos); break;
		case 2: data = EXTRACT10_2(in+byte_pos); break;
		case 3: data = EXTRACT10_3(in+byte_pos); break;
		case 4: data = EXTRACT10_4(in+byte_pos); break;
		case 5: data = EXTRACT10_5(in+byte_pos); break;
		case 6: data = EXTRACT10_6(in+byte_pos); break;
		default:data = EXTRACT10_7(in+byte_pos); break;
		}
		/* Add the fec23 word  */
		data |= fec23_tbl[data]<<10;

		/* write to */
		byte_pos = out_bit_pos>>3;
		switch(out_bit_pos&7) {
		case 0: ENCODE15_0(out+byte_pos,data); break;
		case 1: ENCODE15_1(out+byte_pos,data); break;
		case 2: ENCODE15_2(out+byte_pos,data); break;
		case 3: ENCODE15_3(out+byte_pos,data); break;
		case 4: ENCODE15_4(out+byte_pos,data); break;
		case 5: ENCODE15_5(out+byte_pos,data); break;
		case 6: ENCODE15_6(out+byte_pos,data); break;
		default:ENCODE15_7(out+byte_pos,data); break;
		}
		/* TODO: last word padding ? */
	}
	return 0;
}

/* Unfec23 
 * out: out buffer of BYTE_ALIGNED(nbits)
 * in : input buffer of nbits * 15/10
 * out_of: output offset in bits
 * nbits: size of output in bits
*/
/* libbtbb:
 * comments are the bit that's wrong and the value
 * of diff in air order, from BT spec vol 2, part G, 9: FEC sample data */
static const uint16_t fec23_cor[32] = {
	[0x0b] = 0x01,
	[0x16] = 0x02,
	[0x07] = 0x04,
	[0x0e] = 0x08,
	[0x1c] = 0x10,
	[0x13] = 0x20,
	[0x0d] = 0x40,
	[0x1a] = 0x80,
	[0x1f] = 0x100,
	[0x15] = 0x200
};

static inline uint16_t unfec23_10bits(uint16_t in)
{
	uint16_t data, err, cor;

	data = in & 0x3ff;
	err = 0x1f & ((in>>10)^fec23_tbl[data]);
	cor = fec23_cor[err];
	data ^= cor;
	/* FIXME: handle ber */
	return data;
}


/* Unfec23 15 bytes in -> 10 bytes out, start at bit 2 for obvious reasons */
int unfec23_10bytes(uint8_t *out, uint8_t *in)
{
	uint16_t in1, in2, in3, in4, in5, in6, in7, in8;
	int rc = 0;

	in1 = unfec23_10bits(EXTRACT15_2(in));
	in2 = unfec23_10bits(EXTRACT15_1(in+2));
	in3 = unfec23_10bits(EXTRACT15_0(in+4));
	in4 = unfec23_10bits(EXTRACT15_7(in+5));
	in5 = unfec23_10bits(EXTRACT15_6(in+7));
	in6 = unfec23_10bits(EXTRACT15_5(in+9));
	in7 = unfec23_10bits(EXTRACT15_4(in+11));
	in8 = unfec23_10bits(EXTRACT15_3(in+13));

	ENCODE10_0(out,  in1&0x3ff);
	ENCODE10_2(out+1,in2&0x3ff);
	ENCODE10_4(out+2,in3&0x3ff);
	ENCODE10_6(out+3,in4&0x3ff);
	ENCODE10_0(out+5,in5&0x3ff);
	ENCODE10_2(out+6,in6&0x3ff);
	ENCODE10_4(out+7,in7&0x3ff);
	ENCODE10_6(out+8,in8&0x3ff);

	/* FIXME: return bit error count */
	//return ((in1 | in2 | in3 | in4 | in5 | in6 | in7 | in8) >> 10);
	return 0;

}
int unfec23(uint8_t *out, uint8_t *in, unsigned in_of, unsigned nbits)
{
	unsigned in_bit_pos=in_of, byte_pos, data;
	int out_bit_pos=0;
	int rc = 0;

	/* align output to 10 bits */
	nbits = (10*nbits+9)/10;

	/* clear output buffer */
	memset(out, 0, BYTE_ALIGN(nbits));

	if ((in_of & 7) == 2)
	{
		/* Do 10bytes by 10 bytes */
		for(//out_bit_pos=0, in_bit_pos=in_of
			; out_bit_pos<=(int)nbits-80
			; in_bit_pos+=15*8, out_bit_pos+=10*8)
		{
			unfec23_10bytes(&out[out_bit_pos/8], &in[in_bit_pos/8]);
		}
	}


	// 15 bits in -> 10 output bits
	for(//out_bit_pos=0, in_bit_pos=in_of
		; out_bit_pos<(int)nbits
		; in_bit_pos+=15, out_bit_pos+=10)
	{
		byte_pos = in_bit_pos>>3;
		switch(in_bit_pos&7) {
		case 0: data = EXTRACT15_0(in+byte_pos); break;
		case 1: data = EXTRACT15_1(in+byte_pos); break;
		case 2: data = EXTRACT15_2(in+byte_pos); break;
		case 3: data = EXTRACT15_3(in+byte_pos); break;
		case 4: data = EXTRACT15_4(in+byte_pos); break;
		case 5: data = EXTRACT15_5(in+byte_pos); break;
		case 6: data = EXTRACT15_6(in+byte_pos); break;
		default:data = EXTRACT15_7(in+byte_pos); break;
		}
		data = unfec23_10bits(data);
		rc |= (data>>10);
		data &= 0x3ff;
		/* write output */
		byte_pos = out_bit_pos>>3;
		switch(out_bit_pos&7) {
		case 0: ENCODE10_0(out+byte_pos,data); break;
		case 1: ENCODE10_1(out+byte_pos,data); break;
		case 2: ENCODE10_2(out+byte_pos,data); break;
		case 3: ENCODE10_3(out+byte_pos,data); break;
		case 4: ENCODE10_4(out+byte_pos,data); break;
		case 5: ENCODE10_5(out+byte_pos,data); break;
		case 6: ENCODE10_6(out+byte_pos,data); break;
		default:ENCODE10_7(out+byte_pos,data); break;
		}
		
	}
	return rc;
}
/* Fec13 
 * out: out buffer of BYTE_ALIGNED(nbits*3)
 * in : input buffer of nbits 
 * out_of: output offset in bits, must be multiple of 2
 * nbits: size of input in bits, must be multiple of two
*/
int fec13(uint8_t *out, uint8_t *in, unsigned out_of, unsigned nbits)
{
	unsigned in_bit_pos, out_bit_pos, byte_pos, left;
	uint32_t data;

	if ((out_of|nbits) & 1)
	{
		BB_DEBUG("fec13: invalid arguments\n");
		return -1;
	}

	/* 8 bits in -> 24 bits out */
	for(in_bit_pos = 0, out_bit_pos = out_of
	; in_bit_pos < (nbits&~7)
	; in_bit_pos+=8, out_bit_pos += 24)
	{
		data = fec13_tbl[in[in_bit_pos>>3]];
		byte_pos = out_bit_pos>>3; 
		switch(out_bit_pos&7)
		{
		case 0: ENCODE24_0(out+byte_pos, data); break;
		case 2: ENCODE24_2(out+byte_pos, data); break;
		case 4: ENCODE24_4(out+byte_pos, data); break;
		default: ENCODE24_6(out+byte_pos, data); break;
		}
	}
	left = nbits-in_bit_pos;
	/* write left bits */
	if (left)
	{
		data = fec13_tbl[in[in_bit_pos>>3]];
		data &= (1<<(left*3))-1;
		byte_pos = out_bit_pos>>3;
		switch(out_bit_pos&7)
		{
		case 0: ENCODE24_0(out+byte_pos, data); break;
		case 2: ENCODE24_2(out+byte_pos, data); break;
		case 4: ENCODE24_4(out+byte_pos, data); break;
		default: ENCODE24_6(out+byte_pos, data); break;
		}
	}
	return 0;
}

/* Unfec13 general purpose 
 * out: out buffer of BYTE_ALIGNED(nbits)
 * in : input buffer of nbits * 3
 * in_of: input offset in bits, must be multiple of two
 * nbits: size of output in bits, must be multiple of 2
*/
int unfec13(uint8_t *out, uint8_t *in, unsigned in_of, unsigned nbits)
{
	int be = 0;
	uint16_t li = 0;
	unsigned in_bit_pos, out_bit_pos, byte_pos;
	
	if ((in_of|nbits) & 1)
	{
		BB_DEBUG("Invalid unfec13 of %d\n", in_of);
		return 1;
	}
	/* read 2 output bits (6 input) at the time */
	for (in_bit_pos=in_of, out_bit_pos=0
		; out_bit_pos < nbits
		; in_bit_pos += 6, out_bit_pos += 2)
	{
		/* Read 6 bits from in at bit offset bof */
		byte_pos = in_bit_pos>>3;	// byte idx
		/* bi should be multiple of 2 now */
		switch(in_bit_pos&7){
		case 0: li = unfec13_tbl[EXTRACT6_0(in+byte_pos)]; break;
		case 2:	li = unfec13_tbl[EXTRACT6_2(in+byte_pos)]; break;
		case 4: li = unfec13_tbl[EXTRACT6_4(in+byte_pos)]; break;
		default:li = unfec13_tbl[EXTRACT6_6(in+byte_pos)]; break; // 6
		}
		be += li>>14;
		out[out_bit_pos>>3] |= (li&3)<<(out_bit_pos&7);
	}
	return be;
}
